#include "sys_clock.h"
#include "sys_types.h"
#include "sys_io.h"
#include "reg_ccu.h"
#include "sys_delay.h"
#include "sys_uart.h"

#define CCU_Base_Address        (u32_t)0x01C20000
#define CCU_BUS_CLK_GATING_REG0 (u32_t) CCU_Base_Address + 0x0060
#define CCU_BUS_CLK_GATING_REG1 (u32_t) CCU_Base_Address + 0x0064
#define CCU_BUS_CLK_GATING_REG2 (u32_t) CCU_Base_Address + 0x0068
#define CCU_BUS_SOFT_RST_REG0   (u32_t) CCU_Base_Address + 0x02C0
#define CCU_BUS_SOFT_RST_REG1   (u32_t) CCU_Base_Address + 0x02C4
#define CCU_BUS_SOFT_RST_REG2   (u32_t) CCU_Base_Address + 0x02D0
/*
等待系统时钟成功
*/
static void wait_pll_stable(u32_t base)
{
	u32_t rval = 0;
	u32_t time = 0xfff;

	do
	{
		rval = read32(base);
		time--;
	} while (time && !(rval & (1 << 28)));
}
/*
设置PLL
*/
static void clock_set_pll_cpu(u32_t clk)
{
	u32_t n, k, m, p;
	u32_t rval = 0;
	u32_t div = 0;

	if (clk > 720000000)
		clk = 720000000;

	if ((clk % 24000000) == 0)
	{
		div = clk / 24000000;
		n = div - 1;
		k = 0;
		m = 0;
		p = 0;
	}
	else if ((clk % 12000000) == 0)
	{
		m = 1;
		div = clk / 12000000;
		if ((div % 3) == 0)
			k = 2;
		else if ((div % 4) == 0)
			k = 3;
		else
			k = 1;
		n = (div / (k + 1)) - 1;
		p = 0;
	}
	else
	{
		div = clk / 24000000;
		n = div - 1;
		k = 0;
		m = 0;
		p = 0;
	}

	rval = read32(F1C100S_CCU_BASE + CCU_PLL_CPU_CTRL);
	rval &= ~((0x3 << 16) | (0x1f << 8) | (0x3 << 4) | (0x3 << 0));
	rval |= (1U << 31) | (p << 16) | (n << 8) | (k << 4) | m;
	write32(F1C100S_CCU_BASE + CCU_PLL_CPU_CTRL, rval);
	wait_pll_stable(F1C100S_CCU_BASE + CCU_PLL_CPU_CTRL);
}

#ifndef SYSBOOT0
/*
非BOOT0使用
系统时钟：为24MHZ的整倍数
单位HZ或MHZ
*/
void Sys_Clock_Init(int cpu_frequency)
{
	int cpu_frequency_hz = 0;
	if (cpu_frequency < 1000) // MHZ->HZ
	{
		cpu_frequency_hz = cpu_frequency * 1000000;
	}
	else // HZ
	{
		cpu_frequency_hz = cpu_frequency;
	}
	// 24MHZ倍数
	cpu_frequency_hz = cpu_frequency_hz / 24000000 * 24000000;
	//
	write32(F1C100S_CCU_BASE + CCU_PLL_STABLE_TIME0, 0x1ff);
	write32(F1C100S_CCU_BASE + CCU_PLL_STABLE_TIME1, 0x1ff);
	write32(F1C100S_CCU_BASE + CCU_CPU_CFG, 0x00020000);
	write32(F1C100S_CCU_BASE + CCU_AHB_APB_CFG, 0x00012110);

	clock_set_pll_cpu(cpu_frequency_hz);
}
#else
// 延时
static inline void sdelay(int loops)
{
	int s;
	for (s = 0; s < 300; s++)
		;
}
/*
BOOT0使用
系统时钟：为24MHZ的整倍数
单位HZ或MHZ
*/
void Sys_Clock_Init(int cpu_frequency)
{
	u32_t val;
	int cpu_frequency_hz = 0;
	if (cpu_frequency < 1000) // MHZ->HZ
	{
		cpu_frequency_hz = cpu_frequency * 1000000;
	}
	else // HZ
	{
		cpu_frequency_hz = cpu_frequency;
	}
	// 24MHZ倍数
	cpu_frequency_hz = cpu_frequency_hz / 24000000 * 24000000;
	//
	write32(F1C100S_CCU_BASE + CCU_PLL_STABLE_TIME0, 0x1ff);
	write32(F1C100S_CCU_BASE + CCU_PLL_STABLE_TIME1, 0x1ff);

	val = read32(F1C100S_CCU_BASE + CCU_CPU_CFG);
	val &= ~(0x3 << 16);
	val |= (0x1 << 16);
	write32(F1C100S_CCU_BASE + CCU_CPU_CFG, val);
	sdelay(100);

	write32(F1C100S_CCU_BASE + CCU_PLL_VIDEO_CTRL, 0x81004107);
	sdelay(100);
	write32(F1C100S_CCU_BASE + CCU_PLL_PERIPH_CTRL, 0x80041800);
	sdelay(100);
	// write32(F1C100S_CCU_BASE + CCU_AHB_APB_CFG, 0x00003180);
	write32(F1C100S_CCU_BASE + CCU_AHB_APB_CFG, 0x00012110);
	sdelay(100);

	val = read32(F1C100S_CCU_BASE + CCU_DRAM_CLK_GATE);
	val |= (0x1 << 26) | (0x1 << 24);
	write32(F1C100S_CCU_BASE + CCU_DRAM_CLK_GATE, val);
	sdelay(100);

	clock_set_pll_cpu(cpu_frequency_hz);
	val = read32(F1C100S_CCU_BASE + CCU_CPU_CFG);
	val &= ~(0x3 << 16);
	val |= (0x2 << 16);
	write32(F1C100S_CCU_BASE + CCU_CPU_CFG, val);
	sdelay(100); // 100->200
}
#endif

/*
返回PLL_PERIPH时钟-HZ
*/
unsigned int get_pll_periph_frequency(void)
{
	int reg = read32(F1C100S_CCU_BASE + CCU_PLL_PERIPH_CTRL);
	int N = ((reg >> 8) & 0x1f) + 1;
	int K = ((reg >> 4) & 0x3) + 1;
	int PLL = 24000000 * N * K / 2;
	return PLL;
}
/*
返回系统时钟-HZ
*/
unsigned int get_cpu_frequency(void)
{
	int div[3] = {1, 2, 4};
	int reg = read32(F1C100S_CCU_BASE + CCU_CPU_CFG); // 时钟源
	int cpu_clk_src = (reg >> 16) & 0x3;
	if (cpu_clk_src == 0) // 00: LOSC
	{
		return 32000;
	}
	else if (cpu_clk_src == 1) // 01: OSC24M
	{
		return 24000000;
	}
	else // 1X: PLL_CPU
	{
		reg = read32(F1C100S_CCU_BASE + CCU_PLL_CPU_CTRL); // CPU PLL
		int N = ((reg >> 8) & 0x1f) + 1;
		int K = ((reg >> 4) & 0x3) + 1;
		int M = ((reg >> 0) & 0x3) + 1;
		int P = div[(reg >> 16) & 0x3];
		int PLL = (24000000 * N * K) / (M * P);
		return PLL;
	}
}
/*
返回AHB时钟-HZ
*/
unsigned int get_ahb_frequency(void)
{
	int div[4] = {1, 2, 4, 8};
	int APB_CLK_RATIO[4] = {2, 2, 4, 8};
	int reg = read32(F1C100S_CCU_BASE + CCU_AHB_APB_CFG);
	int ahb_clk_src = (reg >> 12) & 0x3;
	int clk_src = 0;
	if (ahb_clk_src == 0) // 00: LOSC
	{
		clk_src = 32000;
	}
	else if (ahb_clk_src == 1) // 01: OSC24M
	{
		clk_src = 24000000;
	}
	else if (ahb_clk_src == 2) // 10: CPUCLK
	{
		clk_src = get_cpu_frequency();
	}
	else // 11: PLL_PERIPH/AHB_PRE_DIV
	{
		int AHB_PRE_DIV = APB_CLK_RATIO[(reg >> 8) & 0x2];
		int PLL_PERIPH = get_pll_periph_frequency();
		clk_src = PLL_PERIPH / AHB_PRE_DIV;
	}
	return clk_src / div[(reg >> 4) & 0x3];
}
/*
返回APB时钟-HZ
*/
unsigned int get_apb_frequency(void)
{
	int div[4] = {2, 2, 4, 8};
	int ahb_clk = get_ahb_frequency();
	int d = (read32(F1C100S_CCU_BASE + CCU_AHB_APB_CFG) >> 8) & 0x3;
	return ahb_clk / div[d];
}
/*打开外设时钟*/
void Open_Dev_Clock(int Dev)
{
	// 使能时钟
	S_BIT((CCU_BUS_CLK_GATING_REG0 + (((Dev >> 8) & 0x3) * 0x4)), (Dev & 0x1f));
	// 关闭复位
	S_BIT((CCU_BUS_SOFT_RST_REG0 + (((Dev >> 8) & 0x3) * 0x4)), (Dev & 0x1f));
	delay_us(100);
}

/*关闭外设时钟*/
void Close_Dev_Clock(int Dev)
{
	// 关闭时钟
	C_BIT((CCU_BUS_CLK_GATING_REG0 + (((Dev >> 8) & 0x3) * 0x4)), (Dev & 0x1f));
	// 开启复位
	C_BIT((CCU_BUS_SOFT_RST_REG0 + (((Dev >> 8) & 0x3) * 0x4)), (Dev & 0x1f));
	delay_us(100);
}
